home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / FDF101.ARJ / SFDF.C < prev    next >
C/C++ Source or Header  |  1992-04-29  |  9KB  |  425 lines

  1. /*
  2.  * sfdf.c
  3.  *
  4.  * find duplicates.  searches a given path and its sub-directories for
  5.  * duplicate files.  Duplicate files have the same name, size, date and
  6.  * contents.  However, the definition used by this program can be any
  7.  * user-specified combination of the above.
  8.  *
  9.  * Roy Bixler (original development and Atari ST version)
  10.  * Ayman Barakat (idea)
  11.  * David Oertel (MS-DOS version)
  12.  *
  13.  * Version 1.0: March 11, 1991
  14.  * Version 1.01: April 12, 1992
  15.  *
  16.  * This program is free software; you can redistribute it and/or modify
  17.  * it under the terms of the GNU General Public License as published by
  18.  * the Free Software Foundation; either version 1, or (at your option)
  19.  * any later version.
  20.  *
  21.  * This program is distributed in the hope that it will be useful,
  22.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24.  * GNU General Public License for more details.
  25.  *
  26.  * You should have received a copy of the GNU General Public License
  27.  * along with this program; if not, write to the Free Software
  28.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  29.  */
  30.  
  31.  
  32. #include <ctype.h>
  33. #include <dir.h>
  34. #include <dos.h>
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <string.h>
  38.  
  39. #include "fdfcomm.h"
  40. #include "fdfstat.h"
  41. #include "elib.h"
  42. #include "sfdf.h"
  43.  
  44.  
  45.  
  46. /*
  47.  * print_help
  48.  *
  49.  * prints out the help message for the program
  50.  */
  51. void print_help()
  52.  
  53. {
  54.     printf(SFDF_USAGE, PROG_NAME, PROG_NAME);
  55. }
  56.  
  57.  
  58.  
  59. /*
  60.  * show_doc
  61.  *
  62.  * show full documentation
  63.  */
  64. void show_doc()
  65.  
  66. {
  67.     printf(SFDF_SCHPIEL1);
  68.     printf(SFDF_SCHPIEL2);
  69. }
  70.  
  71.  
  72.  
  73. /*
  74.  * sort_eq_match
  75.  *
  76.  * returns non-zero if the sort criteria is also one of the match criteria.
  77.  */
  78. int sort_eq_match()
  79.  
  80. {
  81.     return (((Sort_criteria & NAME_SORT) && (Match_criteria & NAMES_MATCH)) ||
  82.             ((Sort_criteria & SIZE_SORT) && (Match_criteria & SIZES_MATCH)) ||
  83.             ((Sort_criteria & TIME_SORT) && (Match_criteria & TIMES_MATCH)));
  84. }
  85.  
  86.  
  87.  
  88. /*
  89.  * print_dups
  90.  *
  91.  * given a pointer to a name which has been determined to be duplicated, print
  92.  * all the names and their path's out.  Return where a mismatch is found.
  93.  */
  94. void print_dups(FILE_LIST *start)
  95.  
  96. {
  97.     int n_found = 0;
  98.     long n_bytes = 0L;
  99.     FILE_LIST *cur;
  100.  
  101.     for (cur = start->next;
  102.          ((cur != NULL) && ((!sort_eq_match()) || (cmpflist_eq(start, cur))));
  103.          cur = cur->next)
  104.         if ((!cur->printed) && (files_match(start, cur))) {
  105.             if (!n_found++) {
  106.                 n_found++;
  107.                 if (v_flag)
  108.                     n_bytes += start->dta.ff_fsize;
  109.                 print_match_header(start);
  110.                 print_next_match(start, -1);
  111.             }
  112.             if (v_flag)
  113.                 n_bytes += cur->dta.ff_fsize;
  114.             print_next_match(cur, -1);
  115.         }
  116.  
  117.     if (n_found) {
  118.         if (v_flag) {
  119.             update_num_dups(n_found, n_bytes);
  120.             update_num_which_dupd();
  121.         }
  122.         printf("\n");
  123.     }
  124. }
  125.  
  126.  
  127.  
  128. /*
  129.  * gen_id_menu
  130.  *
  131.  * given a starting point in the file list, generate an interactive delete
  132.  * menu.  Return number of items put into the menu.
  133.  */
  134. int gen_id_menu(FILE_LIST *start, FILE_LIST **menu, int max_items)
  135.  
  136. {
  137.     int n_found = 0;
  138.     long n_bytes = 0L;
  139.     FILE_LIST *cur;
  140.  
  141.     for (cur = start->next;
  142.          ((cur != NULL) && (cmpflist_eq(start, cur)) && (n_found < max_items));
  143.          cur = cur->next)
  144.         if ((!cur->printed) && (files_match(start, cur))) {
  145.             if (n_found == 0) {
  146.                 if (v_flag)
  147.                     n_bytes += start->dta.ff_fsize;
  148.                 menu[n_found++] = start;
  149.             }
  150.             if (v_flag)
  151.                 n_bytes += cur->dta.ff_fsize;
  152.             menu[n_found++] = cur;
  153.         }
  154.  
  155.     if ((n_found) && (v_flag)) {
  156.         update_num_which_dupd();
  157.         update_num_dups(n_found, n_bytes);
  158.     }
  159.  
  160.     return n_found;
  161. }
  162.  
  163.  
  164.  
  165. /*
  166.  * id_dups
  167.  *
  168.  * given a pointer to a name which has been determined to be duplicated, print
  169.  * all the names and their path's out and ask the user which ones to delete.
  170.  */
  171. void id_dups(FILE_LIST *start)
  172.  
  173. {
  174.     int n_found, i, n_del;
  175.     FILE_LIST *cur, *menu[N_INTERACTIVE];
  176.     char menu_sel[MAX_STR], which_del[N_INTERACTIVE];
  177.  
  178.     if (!(n_found = gen_id_menu(start, menu, N_INTERACTIVE)))
  179.         return;
  180.     while (1) {
  181.         print_id_menu(menu, n_found);
  182.         printf("\nEnter list of files to delete (hit CR for none)\n");
  183.           fgets(menu_sel, MAX_STR-1, stdin);
  184.           zap_trailing_nl(menu_sel, MAX_STR-1, stdin);
  185.           if (!mark_list(menu_sel, which_del, n_found)) {
  186.              for (n_del=0, i=0; i < n_found; i++)
  187.                   if (which_del[i])
  188.                       if (!delete_path_name_file(menu[i]->path,
  189.                                                 menu[i]->dta.ff_name, '\0')) {
  190.                          n_del++;
  191.                          if (v_flag) {
  192.                               update_total_del_bytes(menu[i]->dta.ff_fsize);
  193.                              if (v_flag > 1) {
  194.                                  printf("Deleted ");
  195.                                  print_fpath(menu[i]->path,
  196.                                              menu[i]->dta.ff_name);
  197.                                  printf("\n");
  198.                              }
  199.                          }
  200.                      }
  201.               break;
  202.           }
  203.       } 
  204.  
  205.      if (n_del)
  206.          printf("\n");
  207.   }
  208.  
  209.  
  210. /*
  211.  * find_non_printed
  212.  *
  213.  * given a pointer to a FILE_LIST, return the pointer to the next element which
  214.  * has not been printed yet.
  215.  */
  216. FILE_LIST *find_non_printed(FILE_LIST *file)
  217.  
  218. {
  219.     for (; ((file != NULL) && (file->printed)); file = file->next);
  220.     return file;
  221. }
  222.  
  223.  
  224.  
  225. /*
  226.  * find_dups
  227.  *
  228.  * given a path, find the duplicate files and dump them to the standard output.
  229.  */
  230. void find_dups()
  231.  
  232. {
  233.     FILE_LIST *f_found;
  234.  
  235.     for (f_found = F_list; (f_found != NULL);
  236.          f_found = find_non_printed(f_found->next))
  237.         if (i_flag)
  238.             id_dups(f_found);
  239.         else
  240.             print_dups(f_found);
  241. }
  242.  
  243.  
  244.  
  245. /*
  246.  * count_total_stats
  247.  *
  248.  * go back over the linked list and count the number of files and the total
  249.  * bytes in the files
  250.  */
  251. void count_total_stats()
  252.  
  253. {
  254.     FILE_LIST *cur;
  255.  
  256.     for (cur = F_list; cur != NULL; cur = cur->next) {
  257.         update_num_files();
  258.         update_total_bytes(cur->dta.ff_fsize);
  259.     }
  260. }
  261.  
  262.  
  263.  
  264. /*
  265.  * get_options
  266.  *
  267.  * get the command-line options, check for consistency and set the appropriate
  268.  * variables.
  269.  */
  270. int get_options(int argc, char **argv)
  271.  
  272. {
  273.     extern int getopt(int argc, char **argv, char *opts);
  274.     extern int Optind;
  275.     extern char *optarg;
  276.     int optchar;
  277.     char a_flag = 0, c_flag = 0, d_flag = 0, n_flag = 0, s_flag = 0;
  278.  
  279.     if (argc < 2) {
  280.         print_help();
  281.         exit(-1);
  282.     }
  283.  
  284.     while ((optchar = getopt(argc, argv, GETOPT_LIST)) != EOF) {
  285.         if (isupper(optchar))
  286.             optchar = tolower(optchar);
  287.         switch (optchar) {
  288.         case 'i':
  289.             i_flag = 1;
  290.             break;
  291.         case 'l':
  292.             l_flag = 1;
  293.             break;
  294.         case 'm':
  295.             if ((optarg == NULL) || (strpbrk(optarg, "AaCcDdNnSs") != optarg)) {
  296.                 printf("%s: must specify 'a' or 'c', 'd', 'n' and/or 's' after -m\n", 
  297.                        PROG_NAME);
  298.                 print_help();
  299.                 exit(-1);
  300.             }
  301.             for (;*optarg != '\0'; optarg++) {
  302.                 if (isupper(*optarg))
  303.                     *optarg = tolower(*optarg);
  304.                 switch (*optarg) {
  305.                 case 'a':
  306.                     a_flag = 1;
  307.                     break;
  308.                 case 'c':
  309.                     c_flag = 1;
  310.                     Match_criteria |= CONTENTS_MATCH;
  311.                     break;
  312.                 case 'd':
  313.                     d_flag = 1;
  314.                     Match_criteria |= TIMES_MATCH;
  315.                     break;
  316.                 case 'n':
  317.                     n_flag = 1;
  318.                     Match_criteria |= NAMES_MATCH;
  319.                     break;
  320.                 case 's':
  321.                     s_flag = 1;
  322.                     Match_criteria |= SIZES_MATCH;
  323.                     break;
  324.                 default:
  325.                     printf("%s: invalid match criteria '%c' specified\n", 
  326.                            PROG_NAME, *optarg);
  327.                     print_help();
  328.                     exit(-1);
  329.                 }
  330.             }
  331.             break;
  332.         case 'o':
  333.             if ((optarg == NULL) || (strpbrk(optarg, "AaDd") != optarg)) {
  334.                 printf("%s: must specify 'a' or 'd' after -o\n", PROG_NAME);
  335.                 print_help();
  336.                 exit(-1);
  337.             }
  338.             if (isupper(*optarg))
  339.                 *optarg = tolower(*optarg);
  340.             if (*optarg == 'a')
  341.                 Sort_order = ASCENDING;
  342.             else if (*optarg == 'd')
  343.                 Sort_order = DESCENDING;
  344.             break;
  345.         case 's':
  346.             if ((optarg == NULL) || (strpbrk(optarg, "DdNnSs") != optarg)) {
  347.                 printf("%s: must specify 'd', 'n' or 's' after -s\n",
  348.                        PROG_NAME);
  349.                 print_help();
  350.                 exit(-1);
  351.             }
  352.             if (isupper(*optarg))
  353.                 *optarg = tolower(*optarg);
  354.             switch (*optarg) {
  355.             case 'd':
  356.                 Sort_criteria = TIME_SORT;
  357.                 break;
  358.             case 'n':
  359.                 Sort_criteria = NAME_SORT;
  360.                 break;
  361.             case 's':
  362.                 Sort_criteria = SIZE_SORT;
  363.                 break;
  364.             }
  365.             break;
  366.         case 'v':
  367.             v_flag++;
  368.             break;
  369.         case '?':
  370.             show_doc();
  371.             exit(0);
  372.         default:
  373.             print_help();
  374.             exit(-1);
  375.         }
  376.     }
  377.  
  378.     if (argc == Optind) {
  379.         printf("%s: at least one path specification required\n", PROG_NAME);
  380.         print_help();
  381.         exit(-1);
  382.     }
  383.     else if (a_flag)
  384.         if ((c_flag) || (d_flag) || (n_flag) || (s_flag)) {
  385.             printf("%s: -ma option conflicts with -mc, -md, -mn or -ms\n",
  386.                    PROG_NAME);
  387.             print_help();
  388.             exit(-1);
  389.         }
  390.         else
  391.             Match_criteria = ALL_MATCH;
  392.     else if (!Match_criteria)
  393.         Match_criteria = (TIMES_MATCH|SIZES_MATCH|NAMES_MATCH);
  394.     else if (Match_criteria & CONTENTS_MATCH)
  395.         Match_criteria |= SIZES_MATCH;
  396.     if (!Sort_criteria)
  397.         Sort_criteria = NAME_SORT;
  398.  
  399.     return Optind;
  400. }
  401.  
  402.  
  403.  
  404. int main(int argc, char **argv)
  405.  
  406. {
  407.     int i;
  408.     char form_path[MAXPATH];
  409.  
  410.     for (i = get_options(argc, argv); i < argc; i++) {
  411.         format_dir(argv[i], '\0', form_path);
  412.         if (v_flag > 1)
  413.             printf("finding duplicates under %s:\n\n", form_path);
  414.         list_files(PROG_NAME, form_path, (char) 1);
  415.     }
  416.  
  417.     find_dups();
  418.     if (v_flag) {
  419.         count_total_stats();
  420.         print_stats();
  421.     }
  422.  
  423.     return(0);
  424. }
  425.